Strict
Import mojo
Import agfx

#Rem
Header: VERLET Physic - group - BODY
#End


#Rem
Summary: TVerletGroup class
#End
Class TVerletGroup

	#Rem
	Summary: list of all assigned points
	#End
	Field points:ArrayList<TVerletPoint> = New ArrayList<TVerletPoint>()
	
	#Rem
	Summary: list of all links - constraints created from points list
	#End
	Field links:ArrayList<TVerletLink> = New ArrayList<TVerletLink>	
	
	#Rem
	Summary: gravity
	#End
	Field gravity:TVector2
	
	#Rem
	Summary: active or inactive group 
	if is false then works like static body
	#End
	Field active:Int
	
	#Rem
	Summary: bounce box
	#End
	Field bbox:Float[4]
	
	#Rem
	Summary: aligned bbox
	#End	
	Field MinX : Float
	Field MinY : Float
	Field MaxX : Float
	Field MaxY : Float
	
	#Rem
	Summary: center of group
	#End
	Field center:TVector2
	
	#Rem
	Summary: constructor
	gravity:TVector2	- gravity
	active:Int = True	- dynamic or static
	#End
	Method New( gravity:TVector2, active:Int = True)
		Self.center = New TVector2()
		Self.gravity = New TVector2(gravity)		
		Self.active = active
		
	End

	#Rem
	Summary: Set gravity for point
	#End
	Method SetGravity:Void(grav:TVector2)
		Self.gravity.Set(grav)
	End Method
	
	#Rem
	Summary: statuc function for create group
	#End
	Function Create:TVerletGroup( gravity:TVector2, active:Int = True)
		Return New TVerletGroup(gravity,active)
	End
	
	#Rem
	Summary: add point to list
	#End
	Method AddPoint:Void( p:TVerletPoint)
		If p Then 
			p.parent = Self
			points.AddLast( p)
		End IF
	End Method
	
	#Rem
	Summary: add link to list
	#End
	Method AddLink:Void( l:TVerletLink)
		If l Then 
			l.parent = Self
			links.AddLast( l)
		End
	End Method	
	
	#Rem
	Summary: Update
	when you don't have used world manager, you need call this manualy for group in Update loop
	#End
	Method Update:Void()
	
		If Not Self.active Then Return
		
		For Local p:TVerletPoint = EachIn points
			
			p.SetGravity(Self.gravity)			
			p.Update()
		Next
		
		For Local l:TVerletLink = EachIn links
			l.Update()
		Next
		
		Self.CalcBoundingBox()
		Self.CalcCenterPoint()		
	End Method

	#Rem
	Summary: rendering debug information
	#End
	Method Render:Void()
		For Local l:TVerletLink = EachIn links
			l.Render()
		Next
		
		For Local p:TVerletPoint = EachIn points
			p.Render()
		Next
		
		SetColor 0,192,0		
		DrawRectOutline(Self.bbox[0], Self.bbox[1], Self.bbox[2], Self.bbox[3])
		
		SetColor 255,0,0
		DrawOval(Self.center.x-2,Self.center.y-2,4,4)
		
	End Method	
	
	#Rem
	Summary: translate group
	x:Float				- impulse x value
	y:Float				- impulse y value
	#End
	Method ApplyImpulse:Void( x:Float,y:Float)
		For Local p:TVerletPoint = EachIn points
			p.Translate( x,y, False)		
		Next
		Self.CalcBoundingBox()
		Self.CalcCenterPoint()
	End Method	
	
	#Rem
	Summary: translate group
	x:Float				- x or delta x
	y:Float				- y or delat y
	reset:Int = False	- = false : works like impulse 
						- = true :  translate object to position
	#End
	Method Translate:Void( x:Float,y:Float, reset:Int = False)
		For Local p:TVerletPoint = EachIn points
			p.Translate( x,y, reset)		
		Next
		Self.CalcBoundingBox()
		Self.CalcCenterPoint()
	End Method	
	
	#Rem
	Summary: Rotate
	dir:Float		angle or angular impulse
	reset:Int = False	if False then works like impulse, when True then is group rotated
	#End
	Method Rotate:Void( dir:Float, reset:Int = False)
		For Local p:TVerletPoint = EachIn points
			p.Rotate( dir, center, reset)
		Next
		Self.CalcBoundingBox()
		Self.CalcCenterPoint()
	End Method	
	
	#Rem
	Summary: help function for update BBOX data
	#End
	Method CalcBoundingBox:Void()
		Self.bbox[0] = $FFFFFFF
		Self.bbox[1] = $FFFFFFF
		Self.bbox[2] = 0
		Self.bbox[3] = 0
		For Local p:TVerletPoint = EachIn points
			Self.bbox[0] = Min( Self.bbox[0], p.pos.x)
			Self.bbox[1] = Min( Self.bbox[1], p.pos.y)
			Self.bbox[2] = Max( Self.bbox[2], p.pos.x)
			Self.bbox[3] = Max( Self.bbox[3], p.pos.y)
		Next
		
		Self.MinX = Self.bbox[0]
		Self.MinY = Self.bbox[1]
		Self.MaxX = Self.bbox[2]
		Self.MaxY = Self.bbox[3]
		
		Self.bbox[2] -= Self.bbox[0]
		Self.bbox[3] -= Self.bbox[1]
	End Method
	
	#Rem
	Summary: Help function for update cenetr position 
	#End
	Method CalcCenterPoint:Void()
		
		Local tmp:TVector2 = New TVector2(0,0)
		
		For Local p:TVerletPoint = EachIn points
			tmp.Add(p.pos)			
		Next
		
		Self.center = VecMult(tmp, 1.0 / points.Size())
		
	End Method
	
	#Rem
	Summary: project group shape to line 
	and return Min, Max range
	#End
	Method ProjectToAxis:Void( Axis : TVector2, MinV : Float[], MaxV : Float[] )
	
		Local DotP : Float =  VecDot( Axis, Self.points.GetFirst().pos )
		
		MinV[ 0 ] = DotP
		MaxV[ 0 ] = DotP
		
		Local i : Int
		
		For Local p:TVerletPoint = EachIn points
		
			DotP =  VecDot( Axis, p.pos )

			MinV[ 0 ] = Min( DotP, MinV[ 0 ])

			MaxV[ 0 ] = Max( DotP, MaxV[ 0 ])

		Next
	End Method
	
	#Rem
	Summary: Check and iterate range collision 
	#End
	Method IterateRangeCollision:Void(range:TBBox)
		
		
		Local p: TVerletPoint
		For p = EachIn Self.points
			p.IterateRangeCollision(range)
		Next
	End
	
	#Rem
	Summary: Create verlet group RECTANGLE 
	#End
	Method CreateRectangle:Void(x:Float,y:Float, width:Float,height:Float,mass:Float,constrain_coef:FLoat)
		Local V1 : TVerletPoint = New TVerletPoint( x, y ,mass,True)
		Local V2 : TVerletPoint = New TVerletPoint( x + width, y ,mass,True)
		Local V3 : TVerletPoint = New TVerletPoint( x + width, y + height ,mass,True)
		Local V4 : TVerletPoint = New TVerletPoint( x, y + height ,mass,True)
		
		Self.AddPoint(V1)
		Self.AddPoint(V2)
		Self.AddPoint(V3)
		Self.AddPoint(V4)
		
		Self.AddLink(New TVerletLink(V1,V2,constrain_coef))
		Self.AddLink(New TVerletLink(V2,V3,constrain_coef))
		Self.AddLink(New TVerletLink(V3,V4,constrain_coef))
		Self.AddLink(New TVerletLink(V4,V1,constrain_coef))
		Self.AddLink(New TVerletLink(V1,V3,constrain_coef))
		Self.AddLink(New TVerletLink(V2,V4,constrain_coef))
	End Method
	
	
	#Rem
	Summary: Create verlet group - ROPE
	#End
	Method CreateRope:Void(x1:Float,y1:Float,active1:Bool,x2:Float,y2:Float,active2:Bool,segments:Int,mass:Float,constrain_coef:Float)
		Local P1:TVector2 =New TVector2(x1,y1)
		Local P2:TVector2 =New TVector2(x2,y2)
		Local V:TVector2 = VecSub(P2,P1)
		V = VecNormalize(V)
		Local length:Float = VecDist(P1,P2)
		Local seg_length:Float = length/segments
		
		For Local i:=0 to segments-1
			Local nP:TVector2 = VecMult(V,i*seg_length)
			nP.Add(P1)			
			Local vP:TVerletPoint =  New TVerletPoint( nP.x, nP.y ,mass,True)			
			Self.AddPoint(vP)
		Next
		
		For Local i:=0 to segments-2
			Local link:TVerletLink = New TVerletLink(Self.points.Get(i),Self.points.Get(i+1),constrain_coef)
			Self.AddLink(link)
		Next
		
		Self.points.Get(0).active = active1
		Self.points.Get(segments-1).active = active2
		
	End Method
	
	Method CreateCircle:Void(cx:Float,cy:Float,active:Bool,radius:Float,segments:Int,mass:Float,constrain_coef:Float)
	
		Local center:TVerletPoint =  New TVerletPoint( cx, cy ,mass,True)
		Self.AddPoint(center)
		
		Local dir:TVector2 = New TVector2(0,1)		
		Local link:TVerletLink
		
		Local seg_angle:Float = 360.0 / segments
		
		
		For Local i:Int = 0 To segments-1
			
			Local pos:TVector2 = VecRotate(dir,i*seg_angle)
			pos.Mult(radius)
			pos.Add(cx,cy)
			Local vP:TVerletPoint =  New TVerletPoint( pos.x,pos.y ,mass,True)			
			Self.AddPoint(vP)
			Local link:TVerletLink = New TVerletLink(center,vP,constrain_coef)
			Self.AddLink(link)
		Next
		
		For Local i:Int = 0 To self.points.Size()-2
			link = New TVerletLink(Self.points.Get(i),Self.points.Get(i+1),constrain_coef)
			Self.AddLink(link)
		Next
		
		link = New TVerletLink(Self.points.Get(1),Self.points.Get(segments),constrain_coef)
		Self.AddLink(link)
			
	End Method
End
